package org.example.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.yubico.webauthn.CredentialRepository;
import com.yubico.webauthn.RegisteredCredential;
import com.yubico.webauthn.data.ByteArray;
import com.yubico.webauthn.data.PublicKeyCredentialDescriptor;
import com.yubico.webauthn.data.PublicKeyCredentialType;
import org.example.dao.entity.UserThird;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.ByteBuffer;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class PasskeyRepository implements CredentialRepository {

    @Resource
    private UserService userService;

    @Resource
    private UserThirdService userThirdService;


    @Override
    public Set<PublicKeyCredentialDescriptor> getCredentialIdsForUsername(String userId) {
        Integer id = Integer.parseInt(userId);
        LambdaQueryWrapper<UserThird> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserThird::getUserId,id);
        List<UserThird> list = userThirdService.list(queryWrapper);
        if (CollectionUtils.isNotEmpty(list)) {
            return list.stream().map(c -> PublicKeyCredentialDescriptor.builder().id(new ByteArray(Base64.getUrlDecoder().decode(c.getCredentialId()))).type(PublicKeyCredentialType.PUBLIC_KEY).build()).collect(Collectors.toSet());
        }
        return Set.of();
    }

    @Override
    public Optional<ByteArray> getUserHandleForUsername(String userId) {
        Integer id = Integer.parseInt(userId);
        byte[] bytes = ByteBuffer.allocate(Integer.BYTES).putLong(id).array();
        return Optional.of(new ByteArray(bytes));
    }

    /**
     * 需要实现
     */
    @Override
    public Optional<String> getUsernameForUserHandle(ByteArray userIdByteArray) {
        Integer userId = ByteBuffer.wrap(userIdByteArray.getBytes()).getInt();
        return Optional.of(String.valueOf(userId));
    }

    /**
     * 需要实现
     */
    @Override
    public Optional<RegisteredCredential> lookup(ByteArray byteArray, ByteArray userIdByteArray) {
        Integer id = ByteBuffer.wrap(userIdByteArray.getBytes()).getInt();
        LambdaQueryWrapper<UserThird> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserThird::getUserId,id);
        List<UserThird> list = userThirdService.list(queryWrapper);
        Optional<UserThird> userThirdRespOptional = list.stream().filter(e -> StringUtils.equals(e.getCredentialId(), byteArray.getBase64Url())).findFirst();
        if (userThirdRespOptional.isPresent()) {
            UserThird userThirdResp = userThirdRespOptional.get();
            return Optional.of(RegisteredCredential.builder()
                    .credentialId(new ByteArray(Base64.getUrlDecoder().decode(userThirdResp.getCredentialId())))
                    .userHandle(userIdByteArray)
                    .publicKeyCose(new ByteArray(Base64.getUrlDecoder().decode(userThirdResp.getCredential())))
                    .build());
        }
        return Optional.empty();
    }

    /**
     * 需要实现
     */
    @Override
    public Set<RegisteredCredential> lookupAll(ByteArray byteArray) {
        Integer id = ByteBuffer.wrap(byteArray.getBytes()).getInt();
        LambdaQueryWrapper<UserThird> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserThird::getUserId,id);
        List<UserThird> list = userThirdService.list(queryWrapper);
        if (CollectionUtils.isNotEmpty(list)) {
            return list.stream().map(c -> RegisteredCredential.builder()
                    .credentialId(new ByteArray(Base64.getUrlDecoder().decode(c.getCredentialId())))
                    .userHandle(byteArray)
                    .publicKeyCose(new ByteArray(Base64.getUrlDecoder().decode(c.getCredential())))
                    .build()).collect(Collectors.toSet());
        }
        return Set.of();
    }
}
